#include "connectiobuffer.hpp"
#include "logger.hpp"

namespace oraysock5 {
static void ioreadcb(struct bufferevent* bev, void* ptr)
{
    ConnectIoBuffer* p = reinterpret_cast<ConnectIoBuffer*>(ptr);
    p->io_readcb();
}

static void iowritecb(struct bufferevent* bev, void* ptr)
{
    ConnectIoBuffer* p = reinterpret_cast<ConnectIoBuffer*>(ptr);
    p->io_writecb();
}

static void ioeventcb(struct bufferevent* bev, short events, void* ptr)
{
    ConnectIoBuffer* p = reinterpret_cast<ConnectIoBuffer*>(ptr);
    p->io_eventcb(events);
}

ConnectIoBuffer::ConnectIoBuffer(std::shared_ptr<EventBase> base)
    : EventBuffer(base, -1, BEV_OPT_CLOSE_ON_FREE)
{
    setcb(ioreadcb, iowritecb, ioeventcb, this);
    enabled(EV_READ | EV_WRITE | EV_PERSIST);
}

std::weak_ptr<ConnectEvent> ConnectIoBuffer::cnev() const
{
    return cnev_;
}

void ConnectIoBuffer::set_cnev(std::weak_ptr<ConnectEvent> cnev)
{
    cnev_ = cnev;
}

void ConnectIoBuffer::io_readcb()
{
    std::shared_ptr<ConnectEvent> shconector = cnev_.lock();
    if (shconector != nullptr) {
        InputResult ret = shconector->input();
        if (ret == InputResult::INITIATIVE_CLOSE) {
            shconector->close(CloseType::CLIENT_CLOSE);
        } else if (ret == InputResult::PASSIVE_CLOSE) {
            shconector->close(CloseType::SERVER_CLOSE);
        }
    }
}

void ConnectIoBuffer::io_writecb()
{
    std::shared_ptr<ConnectEvent> shconector = cnev_.lock();
    if (shconector != nullptr) {
        int ret = shconector->output();
        if (ret == -1) {
            shconector->close(CloseType::CLIENT_CLOSE);
        }
    }
}

void ConnectIoBuffer::io_eventcb(short events)
{
    std::shared_ptr<ConnectEvent> shconector = cnev_.lock();
    if (shconector != nullptr) {
        if (events & BEV_EVENT_CONNECTED) {
            shconector->success();
        } else if (events & BEV_EVENT_EOF) {
            shconector->close(CloseType::SERVER_CLOSE);
        } else if (events & (BEV_EVENT_ERROR | BEV_EVENT_READING | BEV_EVENT_WRITING | BEV_EVENT_TIMEOUT)) {
            if (shconector->dnsbase() != nullptr)
                LOG_WARN << evutil_gai_strerror(bufferevent_socket_get_dns_error(buffer()));
            LOG_WARN << evutil_socket_error_to_string(EVUTIL_SOCKET_ERROR());
            shconector->close(CloseType::CONNECT_FAIL);
        }
    }
}

}
